package cn.regexp.seckill.service.impl;


import cn.hutool.core.util.IdUtil;
import cn.regexp.common.core.domain.SeckillProductVo;
import cn.regexp.common.core.exception.BusinessException;
import cn.regexp.seckill.domain.OrderInfo;
import cn.regexp.seckill.enums.SeckillRedisKeyEnum;
import cn.regexp.seckill.mapper.OrderInfoMapper;
import cn.regexp.seckill.mapper.PayLogMapper;
import cn.regexp.seckill.mapper.RefundLogMapper;
import cn.regexp.seckill.mapper.SeckillProductMapper;
import cn.regexp.seckill.mq.MQConstant;
import cn.regexp.seckill.service.IOrderInfoService;
import cn.regexp.seckill.service.ISeckillProductService;
import cn.regexp.seckill.web.msg.SeckillCodeMsg;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;

/**
 * @author Regexpei
 * @date 2024/7/5 23:33
 * @description 订单信息服务实现类
 */
@Slf4j
@Service
@Transactional
public class OrderInfoServiceImpl implements IOrderInfoService {

    @Autowired
    private ISeckillProductService seckillProductService;
    @Autowired
    private OrderInfoMapper orderInfoMapper;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private PayLogMapper payLogMapper;
    @Autowired
    private RefundLogMapper refundLogMapper;
    @Autowired
    private SeckillProductMapper seckillProductMapper;

    @SuppressWarnings("AlibabaLowerCamelCaseVariableNaming")
    @Autowired
    private RocketMQTemplate rocketMQTemplate;

    @Override
    public String doSeckill(int time, Long seckillId, Long phone) {
        // 更新库存
        int count = seckillProductMapper.decrStock(seckillId);
        if (count == 0) {
            throw new BusinessException(SeckillCodeMsg.REPEAT_SECKILL);
        }

        // 创建订单并存入数据库
        OrderInfo orderInfo = new OrderInfo();
        SeckillProductVo vo = seckillProductService.find(time, seckillId);
        // 通过雪花算法创建订单唯一编号
        String orderNo = IdUtil.getSnowflakeNextIdStr();
        orderInfo.setOrderNo(orderNo);
        BeanUtils.copyProperties(vo, orderInfo);
        orderInfo.setDeliveryAddrId(1L);
        orderInfo.setUserId(phone);
        orderInfo.setProductCount(1);
        orderInfo.setCreateDate(new Date());
        orderInfo.setSeckillDate(new Date());
        orderInfo.setSeckillTime(time);
        orderInfo.setSeckillId(seckillId);
        orderInfoMapper.insert(orderInfo);

        // 将下单用户和秒杀商品信息存到缓存，用于重复下单判断
        redisTemplate.opsForSet()
                .add(SeckillRedisKeyEnum.SECKILL_PRODUCT_SET.getRealKey(time + ""), phone + ":" + seckillId);
        // 将订单信息存入redis，通过 canal 触发
        return orderNo;
    }

    @Override
    public OrderInfo find(String orderNo) {
        // 从 redis 中获取订单信息
        Object objJson = redisTemplate.opsForHash().get(SeckillRedisKeyEnum.SECKILL_ORDER_HASH.getRealKey(""), orderNo);
        return JSON.parseObject(objJson + "", OrderInfo.class);
    }

    @Override
    public void syncStock(Integer time, Long seckillId) {
        // 获取数据库中秒杀商品的库存
        int stockCount = seckillProductMapper.getStockCount(seckillId);
        // 将库存数量同步到 redis 中
        redisTemplate.opsForHash()
                .put(SeckillRedisKeyEnum.SECKILL_STOCK_COUNT_HASH.getRealKey(time + ""), seckillId + "",
                        stockCount + "");
    }

    @Override
    public void cancelOrder(Integer time, Long seckillId, String orderNo) {
        log.info("OrderInfoSeviceImpl#cancelOrder --> 取消订单，回补库存");
        // 回补真实库存（数据库）
        seckillProductMapper.incrStock(seckillId);
        // 回补预库存（redis）
        this.syncStock(time, seckillId);
        // 修改本地标识（map）
        rocketMQTemplate.sendOneWay(MQConstant.CANCEL_SECKILL_OVER_SIGE_TOPIC, seckillId);
        // 修改订单状态
        orderInfoMapper.updateCancelStatus(orderNo, OrderInfo.STATUS_TIMEOUT);
    }
}
